/*++

Copyright (c) 1990-2000  Microsoft Corporation

Module Name:

    device.c - Device handling events for example driver.

Abstract:

    This is a C version of a very simple sample driver that illustrates
    how to use the driver framework and demonstrates best practices.

--*/

#include "driver.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, PortIODeviceCreate)
#pragma alloc_text (PAGE, PortIOEvtDevicePrepareHardware)
#pragma alloc_text (PAGE, PortIOEvtDeviceReleaseHardware)
#endif


NTSTATUS
PortIODeviceCreate(
    PWDFDEVICE_INIT DeviceInit
    )
/*++

Routine Description:

    Worker routine called to create a device and its software resources.

Arguments:

    DeviceInit - Pointer to an opaque init structure. Memory for this
        structure will be freed by the framework when the WdfDeviceCreate
        succeeds. So don't access the structure after that point.

Return Value:

    NTSTATUS

--*/
{
    WDF_OBJECT_ATTRIBUTES           deviceAttributes;
    PDEVICE_CONTEXT                 deviceContext;
    WDF_PNPPOWER_EVENT_CALLBACKS    pnpPowerCallbacks;
    WDFDEVICE                       device;
    NTSTATUS                        status;
    UNICODE_STRING                  ntDeviceName;
    UNICODE_STRING                  win32DeviceName;
    WDF_FILEOBJECT_CONFIG           fileConfig;
    
    PAGED_CODE();
    
    WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);

    //
    // Register pnp/power callbacks so that we can start and stop the timer as 
    // the device gets started and stopped.
    //
    pnpPowerCallbacks.EvtDevicePrepareHardware = PortIOEvtDevicePrepareHardware;
    pnpPowerCallbacks.EvtDeviceReleaseHardware = PortIOEvtDeviceReleaseHardware;
    
    //
    // Register the PnP and power callbacks. Power policy related callbacks will 
    // be registered later in SotwareInit.
    //
    WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);    

    WDF_FILEOBJECT_CONFIG_INIT(
                    &fileConfig,
                    WDF_NO_EVENT_CALLBACK, 
                    WDF_NO_EVENT_CALLBACK, 
                    WDF_NO_EVENT_CALLBACK // not interested in Cleanup
                    );
    
    // Let the framework complete create and close
    fileConfig.AutoForwardCleanupClose = WdfFalse;
    
    WdfDeviceInitSetFileObjectConfig(DeviceInit,
                                     &fileConfig,
                                     WDF_NO_OBJECT_ATTRIBUTES);
    //
    // Create a named deviceobject so that legacy applications can talk to us.
    // Since we are naming the object, we wouldn't able to install multiple
    // instance of this driver. Please note that as per PNP guidelines, we
    // should not name the FDO or create symbolic links. We are doing it because
    // we have a legacy APP that doesn't know how to open an interface.
    //
    RtlInitUnicodeString(&ntDeviceName, GPD_DEVICE_NAME);
    
    status = WdfDeviceInitAssignName(DeviceInit,&ntDeviceName);
    if (!NT_SUCCESS(status)) {
        return status;
    }
    
    WdfDeviceInitSetDeviceType(DeviceInit, GPD_TYPE);

    //
    // Call this if the device is not holding a pagefile
    // crashdump file or hibernate file.
    //
    WdfDeviceInitSetPowerPageable(DeviceInit);

    status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    //
    // Get the device context and initialize it. WdfObjectGet_DEVICE_CONTEXT is an
    // inline function generated by WDF_DECLARE_CONTEXT_TYPE macro in the
    // device.h header file. This function will do the type checking and return
    // the device context. If you pass a wrong object  handle
    // it will return NULL and assert if run under framework verifier mode.
    //
    deviceContext = PortIOGetDeviceContext(device);

    //
    // This values is based on the hardware design.
    // I'm assuming the address is in I/O space for our hardware.
    // Refer http://support.microsoft.com/default.aspx?scid=kb;en-us;Q323595
    // for more info.
    //        
    deviceContext-> PortMemoryType = 1; 

    //
    // Create a device interface so that application can find and talk
    // to us.
    //
    RtlInitUnicodeString(&win32DeviceName, DOS_DEVICE_NAME);
    
    status = WdfDeviceCreateSymbolicLink(
                device,
                &win32DeviceName);

    if (!NT_SUCCESS(status)) {
        return status;
    }

    //
    // Initialize the I/O Package and any Queues
    //
    status = PortIOQueueInitialize(device);

    return status;
}


NTSTATUS
PortIOEvtDevicePrepareHardware(
    __in WDFDEVICE  Device,    
    __in WDFCMRESLIST  ResourcesRaw,    
    __in WDFCMRESLIST  ResourcesTranslated    
    )
/*++

Routine Description:

    This event is called by the Framework when the device is started
    or restarted after a suspend operation.

Arguments:

    Device - Handle to a framework device object.

Return Value:

    NTSTATUS - Failures will result in the device stack being torn down.

--*/
{
    ULONG  i;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR  desc;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR  descTranslated;
    PDEVICE_CONTEXT deviceContext = NULL;
    NTSTATUS status = STATUS_SUCCESS;

    PAGED_CODE();
    
    if ((NULL == ResourcesRaw) || 
        (NULL == ResourcesTranslated)){
        return STATUS_DEVICE_CONFIGURATION_ERROR;        
    }
        
    deviceContext = PortIOGetDeviceContext(Device);
    
    for (i=0; i < WdfCmResourceListGetCount(ResourcesRaw); i++) {
        
        desc = WdfCmResourceListGetDescriptor(ResourcesRaw, i);
        descTranslated =  WdfCmResourceListGetDescriptor(ResourcesTranslated, i);
        
        switch(desc ->Type) {
        case CmResourceTypePort:

            switch(descTranslated -> Type) {
            case CmResourceTypePort:
                deviceContext -> PortWasMapped = FALSE;
                deviceContext -> PortBase = ULongToPtr(descTranslated->u.Port.Start.LowPart);
                deviceContext -> PortCount = descTranslated ->u.Port.Length;
                KdPrint(("Resource Translated Port: (%x) Length: (%d)\n",
                         descTranslated->u.Port.Start.LowPart,
                         descTranslated->u.Port.Length));                        
                break;
            case CmResourceTypeMemory:
                //
                // Map the memory
                //
                deviceContext-> PortBase = (PVOID)
                                    MmMapIoSpace(descTranslated->u.Memory.Start,
                                                 descTranslated->u.Memory.Length,
                                                 MmNonCached);
                deviceContext-> PortCount = descTranslated->u.Memory.Length;

                deviceContext-> PortWasMapped = TRUE;
                
                KdPrint(("Resource Translated Memory: (%x) Length: (%d)\n",
                         descTranslated->u.Memory.Start.LowPart,
                         descTranslated->u.Memory.Length));                        
                break;
            default:
                KdPrint(("Unhandled resource_type (0x%x)\n", descTranslated->Type));
                status = STATUS_UNSUCCESSFUL;
                ASSERTMSG("Unhandled resource_type in start request\n", FALSE);                        
            }
            break;

        case CmResourceTypeMemory:
            deviceContext-> PortBase = (PVOID)
                                    MmMapIoSpace (descTranslated->u.Memory.Start,
                                    descTranslated->u.Memory.Length,
                                    MmNonCached);
            deviceContext-> PortCount = descTranslated->u.Memory.Length;

            deviceContext-> PortWasMapped = TRUE;
                    
            KdPrint(("Resource Translated Memory: (%x) Length: (%d)\n",
                    descTranslated->u.Memory.Start.LowPart,
                    descTranslated->u.Memory.Length));                        
            
            break;
        case CmResourceTypeInterrupt:
        default:
        KdPrint(("Unhandled resource type (0x%x)\n", desc->Type));
        status = STATUS_UNSUCCESSFUL;
        break;
        }

    }

    return status;
}

NTSTATUS
PortIOEvtDeviceReleaseHardware(    
    __in WDFDEVICE  Device,    
    __in WDFCMRESLIST  ResourcesTranslated )
/*++

Routine Description:
s

Arguments:

    Device - Handle to a framework device object.

Return Value:

    NTSTATUS - The driver is not allowed to fail this function.  If it does, the
    device stack will be torn down.

--*/
{
    PDEVICE_CONTEXT deviceContext = NULL;

    UNREFERENCED_PARAMETER(ResourcesTranslated);
    
    deviceContext = PortIOGetDeviceContext(Device);

    PAGED_CODE();
    
    //
    // We received query-remove earlier so free up resources.
    //
    if (deviceContext->PortWasMapped){
        MmUnmapIoSpace(deviceContext->PortBase, deviceContext->PortCount);
        deviceContext->PortWasMapped = FALSE;
    }

    return STATUS_SUCCESS;
}




